##knn主要部分

import numpy as np
from math import sqrt
from collections import Counter
from MachineLearning.metrics import accuracy_score

class KNNClassifier:

    def __init__(self, k):
        """初始化kNN分类器"""
        assert k >= 1, "k must be valid"
        self.k = k
        self._X_train = None
        self._y_train = None

    def fit(self, X_train, y_train):
        """根据训练数据集X_train和y_train训练kNN分类器"""
        assert X_train.shape[0] == y_train.shape[0], \
            "the size of X_train must be equal to the size of y_train"
        assert self.k <= X_train.shape[0], \
            "the size of X_train must be at least k."

        self._X_train = X_train
        self._y_train = y_train
        return self

    def predict(self, X_predict):
        """给定待预测数据集X_predict，返回表示X_predict的结果向量"""
        assert self._X_train is not None and self._y_train is not None, \
                "must fit before predict!"
        assert X_predict.shape[1] == self._X_train.shape[1], \
                "the feature number of X_predict must be equal to X_train"

        y_predict = [self._predict(x) for x in X_predict]
        return np.array(y_predict)

    def _predict(self, x):
        """给定单个待预测数据x，返回x的预测结果值"""
        assert x.shape[0] == self._X_train.shape[1], \
            "the feature number of x must be equal to X_train"

        distances = [sqrt(np.sum((x_train - x) ** 2))
                     for x_train in self._X_train]
        nearest = np.argsort(distances)

        topK_y = [self._y_train[i] for i in nearest[:self.k]]
        print(topK_y)
        List = []
        for item in topK_y:
            item = item.tolist()
            print(type(item))
            List.append(item[0])
        print(topK_y)
        print(List)
        votes = Counter(List)

        return votes.most_common(1)[0][0]

    def score(self, X_test, y_test):
        """根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""

        y_predict = self.predict(X_test)
        return accuracy_score(y_test, y_predict)

    def __repr__(self):
        return "KNN(k=%d)" % self.k


        #寻找最好的超参数k
def get_k(X_train,y_train,X_test,y_test):
    from sklearn.neighbors import KNeighborsClassifier
    best_score = 0.0
    best_k = -1
    for k in range(1, 11):
        knn_clf = KNeighborsClassifier(n_neighbors=k)
        knn_clf.fit(X_train, y_train)
        score = knn_clf.score(X_test, y_test)
        if score > best_score:
            best_k = k
            best_score = score

    print("best_k =", best_k)
    print("best_score =", best_score)
    return best_k
    #
    #
    # #函数封装
    # import numpy as np
    # from math import sqrt
    # from collections import Counter
    #
    # def kNN_classify(k, X_train, y_train, x):
    #     assert 1 <= k <= X_train.shape[0], "k must be valid"
    #     assert X_train.shape[0] == y_train.shape[0], \
    #         "the size of X_train must equal to the size of y_train"
    #     assert X_train.shape[1] == x.shape[0], \
    #         "the feature number of x must be equal to X_train"
    #
    #     distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in X_train]
    #     nearest = np.argsort(distances)
    #
    #     topK_y = [y_train[i] for i in nearest[:k]]
    #     votes = Counter(topK_y)
    #
    #     return votes.most_common(1)[0][0]